package com.tn.openrpc.serialization;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

import org.apache.commons.io.IOUtils;

import flex.messaging.io.SerializationContext;
import flex.messaging.io.amf.Amf3Input;
import flex.messaging.io.amf.Amf3Output;

/**
 * An implementation of <code>Serializer</code> the to serializes and de-serializes objects to and from the AMF format.
 */
public class AmfSerializer implements Serializer<byte[]>
{
  /**
   * {@inheritDoc}
   */
  @Override
  public byte[] serialize(Object subject) throws SerializationException
  {
    if (subject == null)
    {
      return new byte[0];
    }

    ByteArrayOutputStream out = new ByteArrayOutputStream();

    try
    {
      Amf3Output amf3Output = new Amf3Output(getSerializationContext());
      amf3Output.setOutputStream(out);
      amf3Output.writeObject(subject);
      amf3Output.flush();
      amf3Output.close();

      return out.toByteArray();
    }
    catch (IOException e)
    {
      throw new SerializationException("An error occurred deserializing.", e);
    }
    finally
    {
      IOUtils.closeQuietly(out);
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public <S> S deserialize(byte[] source) throws SerializationException
  {
    InputStream in = new ByteArrayInputStream(source);

    try
    {
      Amf3Input amf3Input = new Amf3Input(getSerializationContext());
      amf3Input.setInputStream(in);

      //noinspection unchecked
      return (S)amf3Input.readObject();
    }
    catch (ClassNotFoundException e)
    {
      throw new SerializationException("An error occurred deserializing.", e);
    }
    catch (IOException e)
    {
      throw new SerializationException("An error occurred deserializing.", e);
    }
    finally
    {
      IOUtils.closeQuietly(in);
    }
  }

  /**
   * Initializes the <code>SerializationContext</code>.
   */
  private static SerializationContext getSerializationContext() {

      SerializationContext serializationContext = SerializationContext.getSerializationContext();
      serializationContext.enableSmallMessages = true;
      serializationContext.instantiateTypes = true;
      serializationContext.supportRemoteClass = true;
      serializationContext.legacyCollection = false;

      serializationContext.legacyMap = false;
      serializationContext.legacyXMLDocument = false;

      serializationContext.legacyXMLNamespaces = false;
      serializationContext.legacyThrowable = false;
      serializationContext.legacyBigNumbers = false;

      serializationContext.restoreReferences = false;
      serializationContext.logPropertyErrors = false;
      serializationContext.ignorePropertyErrors = true;

      return serializationContext;
  }
}
